setwd('')

library(dplyr)
library(ggplot2)
library(cobalt)
library(patchwork)
library(cowplot)
library(sf)
library(rnaturalearth)
library(ggmap)
library(ggtern)
library(MatchIt)

# Replication files: Door-to-door campaigns in an electoral autocracy: Evidence from Hungary

# VISUALIZATION

d2d <- read.csv("d2d.csv") 
elhu <- read.csv("elections_hu.csv") ## election outcomes 2006-2019
d2d_time <- read.csv("d2d_timestamp.csv") ## time stamped individual canvassing attempts
d2df_to_max <- read.csv("d2d_full_nooutlier.csv")
hu <- read.csv("telepulesek_grouped.csv") ## town coordinates

#transformations
d2d <- d2d %>% mutate(
  fidesz_share_18 = fidesz_share_18 * 100,
  momentum_share_18 = momentum_share_18 * 100,
  other_share_18 = (jobbik_share_18 + dk_share_18 + lmp_share_18 + mszp_share_18) * 100,
  left_share_18 = (dk_share_18 + mszp_share_18 + lmp_share_18) * 100,
  jobbik_share_18 = jobbik_share_18 * 100,
  turnout_18 = turnout_18 * 100
)

d2d <- d2d %>% mutate(visited = ifelse(!is.na(workday), 1, 0))

d2d$mom_median <- median(d2d$momentum_share_18, na.rm=T)
d2d$fid_median <- median(d2d$fidesz_share_18, na.rm=T)
d2d$other_median <- median(d2d$other_share_18, na.rm=T)

d2d$Momentum18 <- d2d$momentum_share_18/d2d$mom_median
d2d$Fidesz18 <- d2d$fidesz_share_18/d2d$fid_median
d2d$Opposition18 <- d2d$other_share_18/d2d$other_median

d2d$visited <- as.factor(d2d$visited)
d2d_v1 <- d2d %>% filter(visited==1)
d2d_v0 <- d2d %>% filter(visited==0)

# Figure 1 A: coverage of campaign by municipality
d2d_hu <- merge(d2d, hu, by="city")

hunmap <- map_data("world", region = "Hungary")
d2d_geo <- d2d_hu %>% dplyr::select(city, lat, lon, visited) %>%
  group_by(lon, lat, visited) %>%
  summarise(count = n(), .groups = "drop")

fig1a <- ggplot(hunmap) +
  geom_polygon(aes(x = long, y = lat), color = "black", 
               fill = "white", size = 0.8, data = hunmap) +
  geom_point(aes(x = lon, y = lat, shape = visited, size = visited, alpha = visited),
             stroke = 0.5, data = d2d_geo) +
  theme_void() +
  theme(
    axis.title.x = element_blank(),
    axis.text.x = element_blank(),
    axis.text.y = element_blank(),
    axis.title.y = element_blank(),
    legend.position = c(0.7, 0.05),
    legend.title = element_text(size = 12),
    legend.direction = "horizontal",
    legend.text = element_text(size = 12)
  ) + 
  guides(
    shape = guide_legend(override.aes = list(size = 3)),
    size = "none",
    alpha = "none"
  ) +
  scale_shape_manual(values = c(4, 16), name = "", labels = c("Not visited", "Visited")) +
  scale_size_manual(values = c(1, 3), name = "", labels = c("Not visited", "Visited")) +
  scale_alpha_manual(values = c(0.3, 1), name = "", labels = c("Not visited", "Visited"))

#Figure 1 B: Precincts by visit within Budapest I. districts
register_stadiamaps("7fc8b9cb-f860-4bae-a726-e5e27ebd287e") ## API for stadiamaps
bp1 <- c(left=19.020, bottom=47.480, right=19.050, top=47.515)
bp_1 <- get_stadiamap(bp1, maptype="stamen_toner", zoom=18)
bp1_lat <- c(47.501343, 47.50072, 47.500605, 47.504236, 47.504721, 47.506624, 
             47.50666, 47.507496, 47.504872, 47.504872, 47.499907, 47.496172, 47.496242, 
             47.496242, 47.493485, 47.494068, 47.491237, 47.490144, 47.488311)
bp1_long <- c(19.031689, 19.03465, 19.040132, 19.038515, 19.035837, 19.031628, 
              19.035723, 19.028354, 19.024955, 19.024955, 19.030365, 19.035447, 19.029987,
              19.029987, 19.031064, 19.033938, 19.045245, 19.031548, 19.041226)
bp1_precincts <- data.frame(bp1_lat, bp1_long)
bp1_precincts$precinct_id <- c("Budapest I. kerület_1", "Budapest I. kerület_2", "Budapest I. kerület_3", 
                              "Budapest I. kerület_4", "Budapest I. kerület_5", "Budapest I. kerület_6",
                              "Budapest I. kerület_7", "Budapest I. kerület_8", "Budapest I. kerület_9",
                              "Budapest I. kerület_10", "Budapest I. kerület_11", "Budapest I. kerület_12",
                              "Budapest I. kerület_13", "Budapest I. kerület_14",
                              "Budapest I. kerület_16", "Budapest I. kerület_17", "Budapest I. kerület_18",
                              "Budapest I. kerület_19", "Budapest I. kerület_20")
bp1_fin <- merge(bp1_precincts, d2d, by.x="precinct_id")

fig1b <- ggmap(bp_1) + geom_point(data=bp1_fin, aes(bp1_long, bp1_lat, shape=visited), color="black", 
                                    size=2, stroke=1.5) +
  scale_shape_manual(values=c(4, 16)) + theme_void() + 
  theme(axis.title.x = element_blank(),axis.text.x = element_blank(),
        axis.text.y = element_blank(),axis.title.y = element_blank(),
        legend.position = "none") + 
  guides(color = guide_legend(override.aes = list(size = 2))) + coord_fixed(ratio=1)

# Figure 1 C: Ternary map of visited/not visited precincts by relative party strength
fig1c <- ggtern(data=d2d,aes(x=Fidesz18,y=Momentum18, z=Opposition18)) +
  geom_point(aes(color=visited), data=d2d_v0, size=1)+
  geom_point(aes(color=visited), data=d2d_v1, size=1) +
  xlab("") + ylab("") + zlab("") + Tarrowlab("Momentum 2018") + 
  Larrowlab("Fidesz 2018") + Rarrowlab("Opposition (other) 18")  + theme_rgbw() +
  theme(legend.position = "none") +
  scale_color_manual(values=c("grey", "black"), guide="none") + ylim(0.00001,10) + 
  theme_bw() + annotate("text", x=4, y=3.5, z=0.25, label= "Fidesz'18", angle=60, size=4)+ 
  annotate("text", x=0.1, y=0.9, z=0.98, label= "Momentum'18", angle=298, size=4) + 
  annotate("text", x=1, y=0.1, z=1, label= "Other Opposition'18", angle=0, size=4) +
  theme(axis.text = element_text(size=10),
        axis.ticks = element_line())

#ggsave("fig1.png", arrangeGrob(fig1a, fig1b, fig1c, nrow=1), width=12, height=3, dpi=800)

## Appendix figures

# Figure OA1: Turnout in Hungary since 2006
figoa1 <- ggplot(elhu, aes(x = year, y = turnout, group = type, shape = type)) +
  geom_line(size = 1) + 
  geom_point(size = 5) +  # Add points
  geom_text(aes(label = turnout), vjust = -1, size = 4) +  
  theme_bw() +
  xlab("Year") + 
  theme(axis.text.x = element_text(angle = 30, hjust = 1, size = 14),
        axis.text.y = element_text(angle = 0, hjust = 1, size = 14),
        axis.title.y = element_text(angle = 90, hjust = 0.5, size = 14),
        legend.direction = "horizontal", 
        legend.position = c(0.5, 0.9)) + 
  ylab("Turnout Rate (%)") + 
  scale_x_continuous(breaks = unique(elhu$year)) +  
  scale_y_continuous(limits = c(0, 100), breaks = seq(0, 100, by = 10)) +  
  scale_shape_manual(values = c("general" = 15, "EP" = 16))

#ggsave("figoa1.png", dpi=1200, width=7, height=5)

# Figure OA2: Election results in Hungary since 2006
figoa2 <- ggplot(elhu, aes(x = election, y = result, group = party, shape = party)) +
  geom_line(size = 1) + 
  geom_point(size = 5) +  # Add points
  theme_bw() +
  xlab("Election") + 
  theme(axis.text.x = element_text(angle = 30, hjust = 1, size = 14),
        axis.text.y = element_text(angle = 0, hjust = 1, size = 14),
        axis.title.y = element_text(angle = 90, hjust = 0.5, size = 14),
        legend.direction = "horizontal", legend.position = c(0.5, 0.9)) + 
  ylab("Vote Share (%)") + ylim(0, 75) +
  scale_shape_manual(values = c("Fidesz" = 15, "Opposition" = 16, "Jobbik" = 17, "Momentum" = 18))

#ggsave("figoa2.png", dpi=1200, width=7, height=5)

# Figure OA3: campaign in time
figoa3 <- ggplot(d2d_time, aes(x = distance)) +
  geom_histogram(binwidth = 1, fill = "grey44", color = "grey", alpha = 0.7) + 
  geom_vline(xintercept = 0, color = "black", linetype = "dashed") +  
  labs(x = "Days until the 2019 EP election", y = "Attempts (#) per day", title = "") +  
  theme_bw() + geom_text(aes(x = 0, y = 1500, label = "2019 EP election"),  vjust = 1, hjust = 0.2,
                         color = "black", size = 5, angle = 90) +
  stat_density(geom = "line", color = "black") + 
  theme(axis.title.x = element_text(size = 14),  
        axis.title.y = element_text(size = 14),
        axis.text.x = element_text(size=14),
        axis.text.y = element_text(size=14)) 

#ggsave("figoa3.png", dpi=1200, width=7, height=5)

# Figure OA4: contact rate per eligible voters in precincts
d2d_contact <- d2d %>% group_by(precinct_id) %>% summarise(contact = (trial/eligible_v_19)*100,
                                                              talked = (open/eligible_v_19)*100) %>% 
  ungroup() %>% filter(talked < 55 & talked > 0.1) %>%
  mutate(mean_contact = mean(contact, na.rm=T),mean_talked = mean(talked, na.rm=T))


figoa4 <- ggplot(d2d_contact, aes(x = talked)) +
  geom_histogram(binwidth = 1, fill = "grey44", color = "grey", alpha = 0.7) +  
  geom_vline(xintercept = 12.8, color = "black", linetype = "dashed") +  
  labs(x = "Successful contact rate per all eligible voters in precinct (%)", y = "Precinct count", title = "") +  
  theme_bw() + geom_text(aes(x = 13, y = 10, label = "Avg. successful contact rate"),  
                         vjust = 1, hjust = 0.2, color = "black", size = 4, angle = 90) + 
  theme(axis.title.x = element_text(size = 14),  
        axis.title.y = element_text(size = 14),
        axis.text.x = element_text(size=14),
        axis.text.y = element_text(size=14))

#ggsave("figoa4.png", dpi=1200, width=7, height=5)

# Figure OA5: pre- and post-matching balance on matching covariates
match.l1 <- matchit(visited ~ log_vpb + turnout_18 + momentum_share_18 +
                      fidesz_share_18 + other_share_18,
                    data = d2df_to_max, method = "cem")

cov_names <- data.frame(old = c("log_vpb", "momentum_share_18", "fidesz_share_18", "other_share_18",
                        "turnout_18"),
                new = c("Voter Per Building (log)", "Momentum Share'18", "Fidesz Share'18", "Other Share'18",
                        "Turnout'18"))


figoa5 <- love.plot(match.l1, stats = c("mean.diffs"), binary = "std", abs = FALSE, 
                  grid = TRUE, size=c(5,5), 
                  drop.missing = TRUE, var.names=cov_names, 
                  sample.names = c("Unmatched (n=9,983)", "Matched (n=1,023)"), 
                  colors=c("grey58", "black"),
                  title = "Covariate Balance") + theme_bw() + 
  theme(legend.position=c(0.25,0.8),
        legend.title = element_blank(),
        legend.text=element_text(size=8),
        axis.text.y=element_text(size=10)) + 
  guides(color = guide_legend(override.aes = list(size = 6)))

## END OF SCRIPT
